/*******************************************************************************
*  file    : devicemodel.cpp
*  created : 20.08.2013
*  author  : Slyshyk Oleksiy (alexSlyshyk@gmail.com)
*******************************************************************************/

#include "devicemodel.hpp"
#include "rpcclient.hpp"

#include <QStringList>
#include <QTimer>
#include <QDebug>
#include <QBrush>
#include <algorithm>
#include <functional>

#include <qlogger.hpp>
namespace{
QLogger* l = QLoggerEngine::getLogger("core");
}

DeviceModel::DeviceModel(QObject *parent) :
    QAbstractItemModel(parent)
{
    connect(RpcClient::instance(), SIGNAL(lastIncomingUpdated(int)), this, SLOT(lastInfoSize(int)) );
    connect(RpcClient::instance(), SIGNAL(moleListUpdated(int))    , this, SLOT(deviceListSize(int)) );
    connect(RpcClient::instance(), SIGNAL(workingTasksUpdated(int)), this, SLOT(moleTaskSize(int))  );

    update_timer_ = new QTimer(this);
    connect(update_timer_, &QTimer::timeout, [](){RpcClient::instance()->getMoleList();});
    update_timer_->start(10000);
}

int DeviceModel::rowCount(const QModelIndex &parent) const
{
    if(parent.isValid())
        return 0;
    return moles_.size();
}

int DeviceModel::columnCount(const QModelIndex &parent) const
{
    return DM_COLUMN_COUNT;
    Q_UNUSED(parent)
}

Qt::ItemFlags DeviceModel::flags(const QModelIndex &index) const
{
    return Qt::ItemIsSelectable|Qt::ItemIsEnabled|Qt::ItemIsDragEnabled;
    Q_UNUSED(index)
}

QVariant DeviceModel::data(const QModelIndex &index, int role) const
{
    auto last_sms = [&index,this](const QString& txt)->decltype(last_incoming_.begin())
        {
            return std::find_if(last_incoming_.begin(), last_incoming_.end(),
                [&](const IncomingLine& line)
                {
                   QString text = line[INCOMING_TEXT];text.replace(QChar(0x0d),QChar(';'));
                   return ((line[INCOMING_SOURCE]==moles_.at(index.row())[MOLES_PHONE]) &&
                           (text.contains(txt)));
                });
        };

    if (!index.isValid())
        return QVariant();

    if (index.row() >= static_cast<int>(moles_.size()))
        return QVariant();

    if (role == Qt::DisplayRole)
        switch(index.column())
            {
            case DM_PASSWD_IDX:
                return moles_.at(index.row())[MOLES_PASSWD];
            case DM_BAT_CHANGE_IDX:
                return moles_.at(index.row())[MOLES_BAT_CHANGE];
            case DM_ID_IDX:
                return moles_.at(index.row())[MOLES_ID];
            case DM_NAME_IDX:
                return moles_.at(index.row())[MOLES_NAME];
            case DM_PHONE_NUMBER_IDX:
                return moles_.at(index.row())[MOLES_PHONE];
            case DM_MEMO_IDX:
                return moles_.at(index.row())[MOLES_MEMO];
            case DM_STATUS_IDX:
                {
                    QString status;
                    auto incom = last_sms("Krot");
                    if ( incom != last_incoming_.end() )
                        {
                            QStringList lst = (*incom)[INCOMING_TEXT].split(QChar(';'));
                            int signal = 0; int bat = 0;
                            for(const QString& line : lst)
                                {
                                    if(line.contains("SIGNAL"))
                                        signal = line.split(":")[1].remove("%").toInt();
                                    if(line.contains("BAT"))
                                        bat = line.split(":")[1].remove("%").toInt();
                                }
                            status = QString(tr("Сеть:%1%, Батарея:%2% [%3]"))
                                     .arg(signal).arg(bat).arg((*incom)[INCOMING_DATE]);
                        }
                    return status;
                }
                break;
            case DM_LAST_POS_DATE_IDX:
                {
                    auto incom = last_sms("google");
                    if ( incom != last_incoming_.end() )
                        return (*incom)[INCOMING_DATE];
                    return QString();
                }
                break;
            }
    if (role == DM_IDEX_DATA_ROLE)
        {
            return moles_.at(index.row())[MOLES_ID];
        }
    if (role == DM_LAST_INFO_ROLE)
        {
            auto incom = last_sms("Krot");
            if ( incom != last_incoming_.end() )
                {
                    return (*incom)[INCOMING_TEXT];
                }
            return QString();
        }
    if (role == DM_LAST_POS_ROLE)
        {
            auto incom = last_sms("google");
            if ( incom != last_incoming_.end() )
                {
                    return (*incom)[INCOMING_TEXT];
                }
            return QString();
        }
    if (role == DM_LAST_SMS_DATE)
        {
            QString date1, date2;
            auto incom = last_sms("google");
            if ( incom != last_incoming_.end() )
                date1 = (*incom)[INCOMING_DATE];

            incom = last_sms("Krot");
            if ( incom != last_incoming_.end() )
                date2 = (*incom)[INCOMING_DATE];

            return std::max(date1,date2);
        }
    if (role == DM_WORKING_TASKS)
        {
            return QVariant(moleTasks(index.row()).join("\n"));
        }
    if(role == Qt::BackgroundRole)
        return backgroundRole(index.row());

    return QVariant();
}

QBrush DeviceModel::backgroundRole(int row) const
{
    return QBrush(row_colors_[row]);
}

QStringList DeviceModel::moleTasks(int row) const
{
    QStringList tasks;
    if(mole_tasks_.keys().contains(moles_.at(row)[MOLES_ID].toInt()))
        {
            for(const MoleTask& mt : mole_tasks_[moles_.at(row)[MOLES_ID].toInt()])
                {
                    tasks << mt.toString();
                }
        }
    return tasks;
}


QVariant DeviceModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(role == Qt::DisplayRole && orientation == Qt::Horizontal)
        switch(section)
            {
            case DM_NAME_IDX:
                return QString(tr("Имя"));
            case DM_PHONE_NUMBER_IDX:
                return QString(tr("Телефон"));
            case DM_MEMO_IDX:
                return QString(tr("Примечание"));
            case DM_STATUS_IDX:
                return QString(tr("Статус"));
            case DM_BAT_CHANGE_IDX:
                return QString(tr("Замена батареи"));
            }

    if(role == Qt::DisplayRole && orientation == Qt::Vertical)
        return QVariant(section+1);

    return QVariant();
}

QModelIndex DeviceModel::index(int row, int column, const QModelIndex &parent) const
{
    if (!hasIndex(row, column, parent))
        {
            return QModelIndex();
        }

    return createIndex(row, column);
}

QModelIndex DeviceModel::parent(const QModelIndex &index) const
{
    return QModelIndex();
    Q_UNUSED(index)
}

std::vector<MoleLine> DeviceModel::moles() const
{
    return moles_;
}

void DeviceModel::setMoles(const std::vector<MoleLine>& mol)
{
    auto min_size = std::min(moles_.size(),mol.size());
    for(decltype(min_size) i = 0; i < min_size; ++i)
        {
            if(moles_[i] != mol[i])
                {
                    moles_[i] = mol[i];
                    emit dataChanged(createIndex(i,0),createIndex(i,DM_COLUMN_COUNT-1));
                }
        }
    if(mol.size() > moles_.size())
        {
            auto idx = moles_.size();
            bool is_reset = false;
            if(moles_.size() == 0)
                {
                    is_reset = true;
                    this->beginResetModel();
                }
            else
                this->beginInsertRows(QModelIndex(),idx,mol.size()-1);
            moles_.resize(mol.size());
            row_colors_.resize(moles_.size());
            for( ; idx < moles_.size(); ++idx )
                {
                    moles_[idx] = mol[idx];
                    row_colors_[idx] = QColor("white");
                }
            if( is_reset )
                this->endResetModel();
            else
                this->endInsertRows();
        }
    if(mol.size() < moles_.size())
        {
            this->beginRemoveRows(QModelIndex(),mol.size(),moles_.size()-1);
            moles_.resize(mol.size());
            this->endRemoveRows();
        }
}

void DeviceModel::setIncoming(const std::vector<IncomingLine>& incoming)
{
    for(const IncomingLine& in : incoming)
        {
            auto lst_it = std::find_if(last_incoming_.begin(),last_incoming_.end(),
            [&in](const IncomingLine& line)->bool
                    {
                        return
                        (
                            ( in[INCOMING_SOURCE]==line[INCOMING_SOURCE])&&
                            ((in[INCOMING_TEXT].contains("Krot"  ) && line[INCOMING_TEXT].contains("Krot"  )) ||
                             (in[INCOMING_TEXT].contains("google") && line[INCOMING_TEXT].contains("google")) )
                        );
                    });

            if((lst_it == last_incoming_.end()) || ((*lst_it) != in) )
                {
                    if(lst_it == last_incoming_.end())
                        {
                            last_incoming_.push_back(in);
                        }
                    else
                        {
                            (*lst_it) = in;
                        }

                    auto mole = std::find_if(moles_.begin(),moles_.end(),
                                             [&in](const MoleLine& ml)->bool
                                                  {return (in[INCOMING_SOURCE]==ml[MOLES_PHONE]);});
                    if(mole != moles_.end())
                        {
                            QVector<int> roles; roles<<Qt::DisplayRole;
                            if(in[INCOMING_TEXT].contains("Krot"  ))
                                {
                                    emit dataChanged(
                                             createIndex(std::distance(moles_.begin(),mole),DM_STATUS_IDX),
                                             createIndex(std::distance(moles_.begin(),mole),DM_LAST_INFO_IDX),
                                             roles);
                                }

                            if(in[INCOMING_TEXT].contains("google"))
                                emit dataChanged(createIndex(std::distance(moles_.begin(),mole),DM_LAST_POS_IDX),
                                                 createIndex(std::distance(moles_.begin(),mole),DM_LAST_POS_IDX),
                                                 roles);
                        }
                }
        }
}

void DeviceModel::setMoleTasks(const std::vector<MoleTask> &tasks)
{
    for(size_t i = 0; i < moles_.size(); ++i)
        {
            const MoleLine& ml = moles_[i];
            std::vector<MoleTask> ml_tasks(tasks.size());
            auto end_copy = std::copy_if(tasks.begin(),tasks.end(),ml_tasks.begin(),
                                         [&ml](const MoleTask& t)
                                         {return (t.requestNum() == ml[MOLES_PHONE]);});
            ml_tasks.resize(std::distance(ml_tasks.begin(),end_copy));


            if(mole_tasks_[ml[MOLES_ID].toInt()] != ml_tasks)
                {
                    mole_tasks_[ml[MOLES_ID].toInt()] = ml_tasks;
                    QVector<int> roles; roles<<Qt::DisplayRole;
                    emit dataChanged(createIndex(i,DM_TASKS_IDX),createIndex(i,DM_TASKS_IDX),roles);
                }

            int max_task_time = 0;
            QColor row_color = QColor("white");
            for(const MoleTask& mt : mole_tasks_[ml[MOLES_ID].toInt()])
                {
                    int minutes = mt.startTime().secsTo( QDateTime::currentDateTime() ) / 60 ;
                    if (minutes > max_task_time)
                        max_task_time = minutes;
                }
            QString date_str = data(createIndex(i,0),DM_LAST_SMS_DATE).toString();
            QDateTime date = QDateTime::fromString(date_str, "yyyy-MM-dd hh:mm:ss");
            //TODO - заменить прибитую гвоздями константу на настраиваемую
            if( !date.isValid()  || (date.secsTo(QDateTime::currentDateTime()) > (24*60*60) ))
                {
                    row_color = QColor("yellow");
                }
            if(mole_tasks_[ml[MOLES_ID].toInt()].size())
                {
                    //TODO - заменить прибитую гвоздями константу на настраиваемую
                    if(max_task_time > 1470) // 1470 - кол-во минут на 24,5 часа
                        {
                            row_color = QColor("red");
                        }
                    else
                        {
                            row_color = QColor("orange");
                        }
                }

            if(row_color != row_colors_[i])
                {
                    row_colors_[i] = row_color;
                    QVector<int> roles; roles<<Qt::BackgroundRole;
                    emit dataChanged(createIndex(i,0),createIndex(i,DM_COLUMN_COUNT-1),roles);
                }
        }
}

void DeviceModel::deviceListSize(int)
{
    setMoles(RpcClient::instance()->moleList());
    QStringList idx;
    for(const MoleLine& line : moles_)
        idx << line[MOLES_ID];

    if(idx.size())
        {
            RpcClient::instance()->getLastIncoming(idx);
            RpcClient::instance()->getWorkingTasks(idx);
        }
}

void DeviceModel::lastInfoSize(int )
{
    setIncoming(RpcClient::instance()->lastIncoming());
}

void DeviceModel::moleTaskSize(int )
{
    setMoleTasks(RpcClient::instance()->moleTasks());
}

void DeviceModel::onUpdateTime()
{
    RpcClient::instance()->getMoleList();
}

